﻿using System;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Collections;
using System.Drawing.Drawing2D;
using System.Media;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Security.Cryptography;
using System.Globalization;

namespace Reviewer {
    public partial class FrmMain : Form {
        int[,] pieces = new int[26, 2];         // 棋子数组
        int[] top = new int[26];                // 棋子层级数组
        int[] count = new int[14];              // 堆栈数组
        ArrayList moves = new ArrayList();      // 走法数组

        int[,] pOffsets = new int[26, 2];       // 棋子提起目标偏移数值
        int[,] cOffsets = new int[26, 2];       // 棋子提起当前偏移数值

        int stackSelectedIndex = -1;            // 堆栈选中的棋子堆序号
        int stackPieceSelectedIndex = -1;       // 堆栈选中的棋子序号
        int boardPieceSelectedIndex = -1;       // 棋盘上选中的棋子序号
        int boardPieceSelectedX = Constants.PIECES_POS_STACK;   // 棋盘上选中棋子的棋盘X坐标
        int boardPieceSelectedY = Constants.PIECES_POS_STACK;   // 棋盘上选中棋子的棋盘Y坐标

        bool needDrawLastMoveHighlight = true;
        int lastMoveX = Constants.PIECES_POS_STACK; // 最后一步棋的棋盘X坐标
        int lastMoveY = Constants.PIECES_POS_STACK; // 最后一步棋的棋盘Y坐标

        bool hasMosquito = true;    // 是否带蚊子扩充
        bool hasLadybug = true;     // 是否带瓢虫扩充

        bool isTournamentRule = true;
        int isGameOver = Constants.GAME_NONE;

        float centerX = 0;  // 棋盘的中心点的屏幕X坐标
        float centerY = 0;  // 棋盘的中心点的屏幕Y坐标

        bool inEditMode = false;
        private bool InEditMode {
            get { return inEditMode; }
            set {
                inEditMode = value;
                quiteEditModeToolStripMenuItem.Enabled = value;
                btnQuit.Enabled = value;
            }
        }

        int curSide = Constants.SIDE_NONE;
        ArrayList editMoves = new ArrayList();              // 编辑走法数组
        List<Point> hintMoves = new List<Point>();          // 提示走法数组
        List<Point> tmpHintMoves = new List<Point>();       // 提示走法临时数组
        List<Point> tmpHintMoves2 = new List<Point>();      // 提示走法临时数组
        int curEditMoveIndex = -1;

        bool canDragMove = false;
        int tmpStartX = 0;  // 鼠标点击时的屏幕X坐标
        int tmpStartY = 0;  // 鼠标点击时的屏幕Y坐标
        float lastX = 0;    // 鼠标点击时棋盘的中心点的屏幕X坐标
        float lastY = 0;    // 鼠标点击时棋盘的中心点的屏幕Y坐标

        int mouseCoordX = 0;    // 鼠标点击时的棋盘X坐标
        int mouseCoordY = 0;    // 鼠标点击时的棋盘Y坐标

        int curMoveIndex = -1;
        int totalMovesCount = 0;

        bool needDrawSpyWindow = false;
        int[] spyPieces = new int[7];
        bool needDrawMouseCoord = true;
        bool needDrawGameInfo = true;
        string bsid = "";
        string whitePlayer = "";
        string blackPlayer = "";
        string gameType = "";
        string gameTypeName = "";
        string gameResult = "";
        string whiteTime = "";
        string blackTime = "";
        string movesString = "";
        string hgnx = "";

        Font font = new Font("Verdana", 8.25F);
        Font fontMarker = new Font("Verdana", 8.25F, FontStyle.Bold);
        Font fontBold = new Font("Verdana", 8.25F, FontStyle.Bold);
        SolidBrush solidBrush = new SolidBrush(Color.Black);
        Color bgColor = Color.White;
        Brush hintBrush = new SolidBrush(Color.FromArgb(64, 255, 215, 0));
        Brush sideLineBrush = new SolidBrush(Color.FromArgb(61, 61, 61));

        SoundPlayer snd;

        FrmOnlineReviewRoom frmOnlineReviewRoom;
        FrmLocalGamesDatabase frmLocalGamesDatabase;
        FrmSearchMoves frmSearchMoves;
        public FrmGameLists frmGameLists;
        FrmFavGamesDatabase frmFavGamesDatabase;

        DBHelper dbHelper = new DBHelper();
        HGNXHelper helper = new HGNXHelper();

        public FrmMain() {
            InitializeComponent();
            ofdSGF.InitialDirectory = Path.GetTempPath() + "hive.reviewer.cache\\";
            ofdHGN.InitialDirectory = Application.StartupPath;
            this.Icon = Properties.Resources.Bonjour;
            this.MinimumSize = new Size(this.Width, this.Height);

            snd = new SoundPlayer(Properties.Resources.move);
        }

        #region 绘制函数

        private void DrawMouseCoord(Graphics g) {
            if (needDrawMouseCoord) {
                g.DrawString("(" + mouseCoordX + "," + mouseCoordY + ")", font, solidBrush, Constants.STACK_WHITE_TEXT_LEFT + 8, this.ClientSize.Height - pnlStack.Height - 25);
            }
        }

        //private void DrawSpyWindow(Graphics g) {
        //    if (needDrawSpyWindow) {
        //        g.DrawRectangle(Pens.Black, this.Width - Constants.SPY_WINDOW_RIGHT_OFFSET - Constants.SPY_WINDOW_WIDTH, Constants.SPY_WINDOW_TOP, Constants.SPY_WINDOW_WIDTH, Constants.SPY_WINDOW_HEIGHT);
        //        g.FillRectangle(new SolidBrush(bgColor), this.Width - Constants.SPY_WINDOW_RIGHT_OFFSET - Constants.SPY_WINDOW_WIDTH, Constants.SPY_WINDOW_TOP, Constants.SPY_WINDOW_WIDTH, Constants.SPY_WINDOW_HEIGHT);
        //        for (int i = 0; i < 7; i++) {
        //            if (spyPieces[i] == Constants.PIECES_POS_STACK) {
        //                continue;
        //            }

        //            float coordX = this.Width - Constants.SPY_WINDOW_RIGHT_OFFSET - Constants.SPY_WINDOW_WIDTH + Constants.SPY_WINDOW_PIECE_OFFSET_X - i * Constants.PIECE_WIDTH / 2;
        //            float coordY = Constants.SPY_WINDOW_TOP + Constants.SPY_WINDOW_PIECE_OFFSET_Y;
        //            float radius = Constants.PIECE_OFFSET_X + 4;
        //            PointF[] ps = Get6VertexCoords(coordX, coordY, radius);

        //            if (spyPieces[i] < 13) {
        //                g.FillPolygon(new SolidBrush(Color.FromArgb(255, 247, 231)), ps);
        //            } else {
        //                g.FillPolygon(new SolidBrush(Color.Black), ps);
        //            }

        //            Image img = GetPieceImageByIndex(spyPieces[i]);
        //            g.DrawImage(img, coordX - 23, coordY - 26, Constants.PIECE_WIDTH, Constants.PIECE_HEIGHT);
        //            g.DrawPolygon(new Pen(Color.Black, 1), ps);
        //        }
        //    }
        //}

        private void DrawGameInfo(Graphics g) {
            if (needDrawGameInfo && bsid != "") {
                g.DrawString("> Type: " + gameTypeName, font, solidBrush, Constants.GAME_INFO_LEFT, Constants.GAME_INFO_TOP);
                g.DrawString("> White: " + whitePlayer + " (" + whiteTime + ")", font, solidBrush, Constants.GAME_INFO_LEFT, Constants.GAME_INFO_TOP + Constants.GAME_INFO_LINE_HEIGHT);
                g.DrawString("> Black: " + blackPlayer + " (" + blackTime + ")", font, solidBrush, Constants.GAME_INFO_LEFT, Constants.GAME_INFO_TOP + Constants.GAME_INFO_LINE_HEIGHT * 2);
                g.DrawString("> ID: " + bsid, font, solidBrush, Constants.GAME_INFO_LEFT, Constants.GAME_INFO_TOP + Constants.GAME_INFO_LINE_HEIGHT * 3);
                g.DrawString("> Game Result: " + gameResult, font, solidBrush, Constants.GAME_INFO_LEFT, Constants.GAME_INFO_TOP + Constants.GAME_INFO_LINE_HEIGHT * 4);
                g.DrawString("> Current Move: " + (curMoveIndex + 1) + " of " + totalMovesCount, font, solidBrush, Constants.GAME_INFO_LEFT, Constants.GAME_INFO_TOP + Constants.GAME_INFO_LINE_HEIGHT * 5);
            }
        }

        private void DrawBoard(Graphics g, bool isHover) {
            if (!isHover) {
                for (int i = 0; i < 26; i++) {
                    if (pieces[i, 0] != Constants.PIECES_POS_STACK) {
                        if (top[i] == 1) {
                            DrawPiece(g, i, pieces[i, 0], pieces[i, 1]);
                        }
                    }
                }
            } else {
                for (int depth = 2; depth <= 7; depth++) {
                    for (int i = 0; i < 26; i++) {
                        if (pieces[i, 0] != Constants.PIECES_POS_STACK) {
                            if (top[i] == depth) {
                                DrawPiece(g, i, pieces[i, 0], pieces[i, 1]);
                                int count = HasPiece(pieces[i, 0], pieces[i, 1]);
                                if (count > 1 && top[i] == count) {
                                    DrawMarker(g, pieces[i, 0], pieces[i, 1], cOffsets[i, 0], cOffsets[i, 1]);
                                }
                            }
                        }
                    }
                }
            }
        }

        private void DrawMarker(Graphics g, int x, int y, int offsetX, int offsetY) {
            float coordX = centerX + Constants.PIECE_OFFSET_X * x + offsetX;
            float coordY = centerY + Constants.PIECE_OFFSET_Y * y + offsetY;

            g.FillEllipse(Brushes.Gold, coordX - 19, coordY - 14, 12, 12);
            g.DrawString(HasPiece(x, y).ToString(), fontMarker, Brushes.Black, coordX - 18, coordY - 14.5F);
        }

        private void DrawPiece(Graphics g, int index, int x, int y) {
            float coordX = centerX + Constants.PIECE_OFFSET_X * x + cOffsets[index, 0];
            float coordY = centerY + Constants.PIECE_OFFSET_Y * y + cOffsets[index, 1];
            float radius = Constants.PIECE_OFFSET_X + 4;
            PointF[] ps = Get6VertexCoords(coordX, coordY, radius);

            if (index < 13) {
                g.FillPolygon(new SolidBrush(Color.FromArgb(255, 247, 231)), ps);
            } else {
                g.FillPolygon(new SolidBrush(Color.Black), ps);
            }

            Image img = GetPieceImageByIndex(index);
            g.DrawImage(img, coordX - 23, coordY - 26, 46, 53);
            g.DrawPolygon(new Pen(Color.Black, 1), ps);
        }

        private void DrawStack(Graphics g) {
            g.DrawString("White", (curSide == Constants.SIDE_WHITE) ? fontBold : font, solidBrush, Constants.STACK_WHITE_TEXT_LEFT, Constants.STACK_TEXT_TOP);
            g.DrawString("Black:", (curSide == Constants.SIDE_BLACK) ? fontBold : font, solidBrush, pnlStack.Width / 2, Constants.STACK_TEXT_TOP);

            if (curSide == Constants.SIDE_WHITE) {
                g.FillRectangle(sideLineBrush, 0, 0, pnlStack.Width / 2, 3);
            } else {
                g.FillRectangle(sideLineBrush, pnlStack.Width / 2, 0, pnlStack.Width / 2, 3);
            }
            DrawStackPiece(g, count);
        }

        private void DrawStackSelected(Graphics g) {
            if (stackSelectedIndex != -1) {
                float coordX = Constants.STACK_WHITE_LEFT + stackSelectedIndex * Constants.PIECE_WIDTH;
                float coordY = Constants.STACK_PIECE_TOP;

                if (stackSelectedIndex >= 7) {
                    coordX = pnlStack.Width - Constants.STACK_BLACK_RIGHT_OFFSET + (stackSelectedIndex - 7) * Constants.PIECE_WIDTH;
                }

                float radius = Constants.PIECE_OFFSET_X + 3.5F;
                PointF[] ps = Get6VertexCoords(coordX, coordY, radius);

                g.DrawPolygon(new Pen(Color.Blue, 2.5F), ps);
            }
        }

        private void DrawLastMoveHighlight(Graphics g, bool isHover) {
            if (needDrawLastMoveHighlight) {
                if (lastMoveX == Constants.PIECES_POS_STACK || lastMoveY == Constants.PIECES_POS_STACK) {
                    return;
                }
                if (!isHover) {
                    if (HasPiece(lastMoveX, lastMoveY) == 1) {
                        int index = GetBoardTopPieceIndex(lastMoveX, lastMoveY);
                        DrawHexBoarder(g, lastMoveX, lastMoveY, Color.Lime, cOffsets[index, 0], cOffsets[index, 1]);
                    }
                } else {
                    if (HasPiece(lastMoveX, lastMoveY) > 1) {
                        int index = GetBoardTopPieceIndex(lastMoveX, lastMoveY);
                        DrawHexBoarder(g, lastMoveX, lastMoveY, Color.Lime, cOffsets[index, 0], cOffsets[index, 1]);
                    }
                }
            }
        }

        private void DrawBoardSelected(Graphics g, bool isHover) {
            if (boardPieceSelectedIndex != -1) {
                if (boardPieceSelectedX == Constants.PIECES_POS_STACK || boardPieceSelectedY == Constants.PIECES_POS_STACK) {
                    return;
                }
                if (!isHover) {
                    if (HasPiece(boardPieceSelectedX, boardPieceSelectedY) == 1) {
                        DrawHexBoarder(g, boardPieceSelectedX, boardPieceSelectedY, Color.Blue, cOffsets[boardPieceSelectedIndex, 0], cOffsets[boardPieceSelectedIndex, 1]);
                    }
                } else {
                    if (HasPiece(boardPieceSelectedX, boardPieceSelectedY) > 1) {
                        DrawHexBoarder(g, boardPieceSelectedX, boardPieceSelectedY, Color.Blue, cOffsets[boardPieceSelectedIndex, 0], cOffsets[boardPieceSelectedIndex, 1]);
                    }
                }
            }
        }

        private void DrawStackPiece(Graphics g, int[] count) {
            for (int i = 0; i < count.Length; i++) {
                if (count[i] > 0) {
                    float coordX = Constants.STACK_WHITE_LEFT + i * Constants.PIECE_WIDTH;
                    float coordY = Constants.STACK_PIECE_TOP;

                    if (i >= 7) {
                        coordX = pnlStack.Width - Constants.STACK_BLACK_RIGHT_OFFSET + (i - 7) * Constants.PIECE_WIDTH;
                    }

                    // 当没有蚊子只有瓢虫的时候将瓢虫移到蚊子的位置上
                    if (!hasMosquito && hasLadybug) {
                        if (i == 6 || i == 13) {
                            coordX -= Constants.PIECE_WIDTH;
                        }
                    }

                    float radius = Constants.PIECE_OFFSET_X + 4;
                    PointF[] ps = Get6VertexCoords(coordX, coordY, radius);

                    if (i < 7) {
                        g.FillPolygon(new SolidBrush(Color.FromArgb(255, 247, 231)), ps);
                    } else {
                        g.FillPolygon(new SolidBrush(Color.Black), ps);
                    }

                    Image img = GetStackPieceImageByIndex(i);
                    g.DrawImage(img, coordX - 23, coordY - 26, Constants.PIECE_WIDTH, Constants.PIECE_HEIGHT);
                    g.DrawPolygon(new Pen(Color.Black, 1), ps);
                    if (count[i] > 1) {
                        g.DrawString(count[i].ToString(), font, solidBrush, coordX + 15, coordY + 18);
                    }
                }
            }
        }

        private void DrawHintMoves(Graphics g, bool isHover) {
            for (int i = 0; i < hintMoves.Count; i++) {
                if (!isHover) {
                    if (HasPiece(hintMoves[i].X, hintMoves[i].Y) <= 1) {
                        float coordX = centerX + Constants.PIECE_OFFSET_X * hintMoves[i].X;
                        float coordY = centerY + Constants.PIECE_OFFSET_Y * hintMoves[i].Y;
                        float radius = Constants.PIECE_OFFSET_X + 3.5F;
                        PointF[] ps = Get6VertexCoords(coordX, coordY, radius);

                        g.FillPolygon(hintBrush, ps);
                        g.DrawPolygon(new Pen(Color.Gold, 2.5F), ps);
                    }
                } else {
                    if (HasPiece(hintMoves[i].X, hintMoves[i].Y) > 1) {
                        int index = GetBoardTopPieceIndex(hintMoves[i].X, hintMoves[i].Y);
                        float coordX = centerX + Constants.PIECE_OFFSET_X * hintMoves[i].X + cOffsets[index, 0];
                        float coordY = centerY + Constants.PIECE_OFFSET_Y * hintMoves[i].Y + cOffsets[index, 1];
                        float radius = Constants.PIECE_OFFSET_X + 3.5F;
                        PointF[] ps = Get6VertexCoords(coordX, coordY, radius);

                        g.FillPolygon(hintBrush, ps);
                        g.DrawPolygon(new Pen(Color.Gold, 2.5F), ps);
                    }
                }
            }
        }

        private void DrawGameOver(Graphics g, bool isHover) {
            if (isGameOver > Constants.GAME_NONE) {
                int wQueenX = pieces[0, 0];
                int wQueenY = pieces[0, 1];
                int bQueenX = pieces[13, 0];
                int bQueenY = pieces[13, 1];
                List<Point> neighbors;

                if (isGameOver == Constants.GAME_WHITE_WINS || isGameOver == Constants.GAME_DRAWS) {
                    neighbors = GetNeighborPoints(wQueenX, wQueenY);
                    foreach (Point p in neighbors) {
                        if (!isHover) {
                            if (HasPiece(p.X, p.Y) == 1) {
                                DrawHexBoarder(g, p.X, p.Y, Color.Red);
                            }
                        } else {
                            if (HasPiece(p.X, p.Y) > 1) {
                                int index = GetBoardTopPieceIndex(p.X, p.Y);
                                DrawHexBoarder(g, p.X, p.Y, Color.Red, cOffsets[index, 0], cOffsets[index, 1]);
                            }
                        }
                    }
                }
                if (isGameOver == Constants.GAME_BLACK_WINS || isGameOver == Constants.GAME_DRAWS) {
                    neighbors = GetNeighborPoints(bQueenX, bQueenY);
                    foreach (Point p in neighbors) {
                        if (!isHover) {
                            if (HasPiece(p.X, p.Y) == 1) {
                                DrawHexBoarder(g, p.X, p.Y, Color.Red);
                            }
                        } else {
                            if (HasPiece(p.X, p.Y) > 1) {
                                int index = GetBoardTopPieceIndex(p.X, p.Y);
                                DrawHexBoarder(g, p.X, p.Y, Color.Red, cOffsets[index, 0], cOffsets[index, 1]);
                            }
                        }
                    }
                }
            }
        }

        private void DrawEditMode(Graphics g) {
            if (InEditMode) {
                Rectangle rect = new Rectangle((this.ClientSize.Width - 120) / 2, pnlStack.Top - 20, 120, 20);
                g.FillRectangle(sideLineBrush, rect);
                g.DrawString(" Edit Mode ", fontBold, Brushes.White, rect.X + 28, rect.Y + 4);
            }
        }

        private void DrawHexBoarder(Graphics g, int xIndex, int yIndex, Color color) {
            float coordX = centerX + Constants.PIECE_OFFSET_X * xIndex;
            float coordY = centerY + Constants.PIECE_OFFSET_Y * yIndex;
            float radius = Constants.PIECE_OFFSET_X + 3.5F;
            PointF[] ps = Get6VertexCoords(coordX, coordY, radius);

            g.DrawPolygon(new Pen(color, 2.5F), ps);
        }

        private void DrawHexBoarder(Graphics g, int xIndex, int yIndex, Color color, int offsetX, int offsetY) {
            float coordX = centerX + Constants.PIECE_OFFSET_X * xIndex + offsetX;
            float coordY = centerY + Constants.PIECE_OFFSET_Y * yIndex + offsetY;
            float radius = Constants.PIECE_OFFSET_X + 3.5F;
            PointF[] ps = Get6VertexCoords(coordX, coordY, radius);

            g.DrawPolygon(new Pen(color, 2.5F), ps);
        }

        private Image GetStackPieceImageByIndex(int index) {
            switch (index) {
                case 0: return Properties.Resources.wq;
                case 1: return Properties.Resources.wa;
                case 2: return Properties.Resources.wg;
                case 3: return Properties.Resources.wb;
                case 4: return Properties.Resources.ws;
                case 5: return Properties.Resources.wm;
                case 6: return Properties.Resources.wl;
                case 7: return Properties.Resources.bq;
                case 8: return Properties.Resources.ba;
                case 9: return Properties.Resources.bg;
                case 10: return Properties.Resources.bb;
                case 11: return Properties.Resources.bs;
                case 12: return Properties.Resources.bm;
                case 13: return Properties.Resources.bl;
            }
            return null;
        }

        private Image GetPieceImageByIndex(int index) {
            switch (index) {
                case 0: return Properties.Resources.wq;
                case 1:
                case 2:
                case 3: return Properties.Resources.wa;
                case 4:
                case 5:
                case 6: return Properties.Resources.wg;
                case 7:
                case 8: return Properties.Resources.wb;
                case 9:
                case 10: return Properties.Resources.ws;
                case 11: return Properties.Resources.wm;
                case 12: return Properties.Resources.wl;
                case 13: return Properties.Resources.bq;
                case 14:
                case 15:
                case 16: return Properties.Resources.ba;
                case 17:
                case 18:
                case 19: return Properties.Resources.bg;
                case 20:
                case 21: return Properties.Resources.bb;
                case 22:
                case 23: return Properties.Resources.bs;
                case 24: return Properties.Resources.bm;
                case 25: return Properties.Resources.bl;
            }
            return null;
        }

        #endregion

        #region 生成走法

        /// <summary>
        /// 生成制定棋子的放置走法。
        /// </summary>
        /// <param name="pIndex">棋子的序号</param>
        private void CreatePlaceHintMoves(int pIndex) {
            hintMoves.Clear();
            int side = pIndex < 13 ? Constants.SIDE_WHITE : Constants.SIDE_BLACK;

            if (isTournamentRule) {
                // 黑白双方的第一步都不能放蜂王。
                if ((curMoveIndex == -1 && curEditMoveIndex == -1 && stackPieceSelectedIndex == 0) ||
                   (curMoveIndex == -1 && curEditMoveIndex == 0 && stackPieceSelectedIndex == 13) ||
                   (curMoveIndex == 0 && curEditMoveIndex == -1 && stackPieceSelectedIndex == 13)) {
                    //hintMoves.Clear();
                    //RedrawBoard();
                    return;
                }
            }

            // 第四步都没下蜂王不能放其他子。
            if ((curMoveIndex + curEditMoveIndex == 4 && !IsQueenPlaced(Constants.SIDE_WHITE) && pIndex != 0) ||
               (curMoveIndex + curEditMoveIndex == 5 && !IsQueenPlaced(Constants.SIDE_BLACK) && pIndex != 13)) {
                //hintMoves.Clear();
                //RedrawBoard();
                return;
            }

            // 制定第一步放置的位置。
            if (curMoveIndex == -1 && curEditMoveIndex == -1) {
                hintMoves.Add(new Point(0, 0));
                return;
            }

            // 制定第二步放置的位置。
            if (curMoveIndex + curEditMoveIndex == -1) {
                hintMoves.Add(new Point(2, 0));
                return;
            }

            // 不包含扩充不生成扩充棋子的放入走法。
            if ((pIndex == 11 && !hasMosquito) ||
                (pIndex == 24 && !hasMosquito) ||
                (pIndex == 12 && !hasLadybug) ||
                (pIndex == 25 && !hasLadybug)) {
                return;
            }

            // 遍历已经放入棋盘中的己方棋子，获取每个棋子的相邻6个位置。
            // 遍历每个位置，如果该位置没有放子，并且没有和对方棋子相邻则添加到提示走法数组中。
            int start = pIndex < 13 ? 0 : 13;
            int end = pIndex < 13 ? 13 : 26;
            for (int i = start; i < end; i++) {
                if (pieces[i, 0] == Constants.PIECES_POS_STACK) {
                    continue;
                }
                List<Point> ps = GetNeighborPoints(pieces[i, 0], pieces[i, 1]);
                for (int j = 0; j < 6; j++) {
                    if (HasPiece(ps[j].X, ps[j].Y) > 0 || IsHexAdjacentOpponent(ps[j].X, ps[j].Y, side)) {
                        continue;
                    }
                    if (!IsListContainsPoint(hintMoves, ps[j].X, ps[j].Y)) {
                        hintMoves.Add(ps[j]);
                    }
                }
            }

            // 修正白方蜂王放置的位置为标准坐标系下的位置。
            if (pIndex == 0) {
                for (int i = hintMoves.Count - 1; i >= 0; i--) {
                    if (hintMoves[i].Y < 0) {
                        hintMoves.RemoveAt(i);
                    }
                }
            }

            // 修正当白方蜂王Y坐标为0时黑方蜂王放置的位置
            if (pIndex == 13) {
                if (pieces[0, 1] == 0) {
                    for (int i = hintMoves.Count - 1; i >= 0; i--) {
                        if (hintMoves[i].Y < 0) {
                            hintMoves.RemoveAt(i);
                        }
                    }
                }
            }
        }

        /// <summary>
        /// 生成指定棋子的所有走法（放置走法和移动走法）。
        /// </summary>
        /// <param name="pIndex">棋子的序号</param>
        /// <param name="x">棋子的棋盘X坐标</param>
        /// <param name="y">棋子的棋盘Y坐标</param>
        private void CreateHintMoves(int pIndex, int x, int y) {
            hintMoves.Clear();

            // 如果该棋子被压住则不生成走法。
            if (HasPiece(x, y) != top[pIndex]) {
                return;
            }
            switch (pIndex) {
                case 0:
                case 13: CreateQueenHintMoves(pIndex, x, y); break;
                case 1:
                case 2:
                case 3:
                case 14:
                case 15:
                case 16: CreateAntHintMoves(pIndex, x, y); break;
                case 4:
                case 5:
                case 6:
                case 17:
                case 18:
                case 19: CreateGasshopperHintMoves(pIndex, x, y); break;
                case 7:
                case 8:
                case 20:
                case 21: CreateBeetleHintMoves(pIndex, x, y); break;
                case 9:
                case 10:
                case 22:
                case 23: CreateSpiderHintMoves(pIndex, x, y); break;
                case 11:
                case 24: if (hasMosquito) { CreateMosquitoHintMoves(pIndex, x, y); } break;
                case 12:
                case 25: if (hasLadybug) { CreateLadybugHintMoves(pIndex, x, y); } break;
            }
        }

        /// <summary>
        /// 是否已经放下了蜂王。
        /// </summary>
        /// <param name="side">走棋方</param>
        /// <returns></returns>
        private bool IsQueenPlaced(int side) {
            if (side == Constants.SIDE_WHITE) {
                return pieces[0, 0] != Constants.PIECES_POS_STACK;
            } else {
                return pieces[13, 0] != Constants.PIECES_POS_STACK;
            }
        }

        /// <summary>
        /// 判断是否移动该棋子会破坏棋子的连续性。
        /// </summary>
        /// <param name="pIndex">棋子的序号</param>
        /// <returns></returns>
        private bool IsMoveCanBreak(int pIndex) {
            int[,] pics = new int[26, 2];
            for (int i = 0; i < 26; i++) {
                pics[i, 0] = pieces[i, 0];
                pics[i, 1] = pieces[i, 1];
            }
            pics[pIndex, 0] = Constants.PIECES_POS_STACK;
            pics[pIndex, 1] = Constants.PIECES_POS_STACK;

            int firtsIndex = GetFirstPieceIndex(pics);
            CheckNeighbors(pics, firtsIndex, pics[firtsIndex, 0], pics[firtsIndex, 1]);

            // 判断经过遍历赋值后的棋子数组是否全部位置棋子的位置都设置为棋子堆栈。
            // 如果有棋子（X）的位置不是棋子堆栈，说明如果待判断的棋子拿走会造成（X）棋子连接不到某些棋子，
            // 也就是如果移动了待判断棋子就会破坏棋子的连续性。
            for (int i = 0; i < 26; i++) {
                if (pics[i, 0] != Constants.PIECES_POS_STACK) {
                    return true;
                }
            }
            return false;
        }

        /// <summary>
        /// 判断是否能从一个点移动到另一个点。
        /// </summary>
        /// <param name="fX">起始点的X坐标</param>
        /// <param name="fY">起始点的Y坐标</param>
        /// <param name="tX">结束点的X坐标</param>
        /// <param name="tY">结束点的Y坐标</param>
        /// <returns></returns>
        private bool CanNotMoveIn(int fX, int fY, int tX, int tY) {
            int offsetX = tX - fX;
            int offsetY = tY - fY;
            int top = HasPiece(fX, fY);
            int top2 = HasPiece(tX, tY) + 1;

            if (offsetX == 2 && offsetY == 0) {
                if (top > top2) {
                    return !(HasPiece(fX + 1, fY - 1) < top || HasPiece(fX + 1, fY + 1) < top);
                } else {
                    return !(HasPiece(fX + 1, fY - 1) < top2 || HasPiece(fX + 1, fY + 1) < top2);
                }
            }
            if (offsetX == 1 && offsetY == 1) {
                if (top > top2) {
                    return !(HasPiece(fX + 2, fY) < top || HasPiece(fX - 1, fY + 1) < top);
                } else {
                    return !(HasPiece(fX + 2, fY) < top2 || HasPiece(fX - 1, fY + 1) < top2);
                }
            }
            if (offsetX == -1 && offsetY == 1) {
                if (top > top2) {
                    return !(HasPiece(fX + 1, fY + 1) < top || HasPiece(fX - 2, fY) < top);
                } else {
                    return !(HasPiece(fX + 1, fY + 1) < top2 || HasPiece(fX - 2, fY) < top2);
                }
            }
            if (offsetX == -2 && offsetY == 0) {
                if (top > top2) {
                    return !(HasPiece(fX - 1, fY + 1) < top || HasPiece(fX - 1, fY - 1) < top);
                } else {
                    return !(HasPiece(fX - 1, fY + 1) < top2 || HasPiece(fX - 1, fY - 1) < top2);
                }
            }
            if (offsetX == -1 && offsetY == -1) {
                if (top > top2) {
                    return !(HasPiece(fX - 2, fY) < top || HasPiece(fX + 1, fY - 1) < top);
                } else {
                    return !(HasPiece(fX - 2, fY) < top2 || HasPiece(fX + 1, fY - 1) < top2);
                }
            }
            if (offsetX == 1 && offsetY == -1) {
                if (top > top2) {
                    return !(HasPiece(fX - 1, fY - 1) < top || HasPiece(fX + 2, fY) < top);
                } else {
                    return !(HasPiece(fX - 1, fY - 1) < top2 || HasPiece(fX + 2, fY) < top2);
                }
            }
            return true;
        }

        /// <summary>
        /// 获取棋子数组中在棋盘上的棋子的最小序号。
        /// </summary>
        /// <param name="pics">棋子数组</param>
        /// <returns></returns>
        private int GetFirstPieceIndex(int[,] pics) {
            for (int i = 0; i < 26; i++) {
                if (pics[i, 0] != Constants.PIECES_POS_STACK) {
                    return i;
                }
            }
            return -1;
        }

        /// <summary>
        /// 从指定棋子开始，递归搜索该棋子所连接的所有棋子，并把棋子数组中遍历到的棋子的位置设置为棋子堆栈。
        /// </summary>
        /// <param name="pics">棋子数组</param>
        /// <param name="pIndex">棋子的序号</param>
        /// <param name="x">棋子的棋盘X坐标</param>
        /// <param name="y">棋子的棋盘Y坐标</param>
        private void CheckNeighbors(int[,] pics, int pIndex, int x, int y) {
            if (pics[pIndex, 0] != Constants.PIECES_POS_STACK) {
                pics[pIndex, 0] = Constants.PIECES_POS_STACK;
                pics[pIndex, 1] = Constants.PIECES_POS_STACK;
                List<Point> neighbors = GetNeighborPoints(x, y);
                foreach (Point p in neighbors) {
                    List<int> ps = GetPiecesIndexesAt(p.X, p.Y);
                    if (ps.Count > 0) {
                        foreach (int index in ps) {
                            CheckNeighbors(pics, index, pics[index, 0], pics[index, 1]);
                        }
                    }
                }
            }
            return;
        }

        /// <summary>
        /// 获取该棋盘坐标下的所有棋子。
        /// </summary>
        /// <param name="x">棋盘的X坐标</param>
        /// <param name="y">棋盘的Y坐标</param>
        /// <returns></returns>
        private List<int> GetPiecesIndexesAt(int x, int y) {
            List<int> list = new List<int>();
            for (int i = 0; i < 26; i++) {
                if (pieces[i, 0] == x && pieces[i, 1] == y) {
                    list.Add(i);
                }
            }
            return list;
        }

        /// <summary>
        /// 判断是否能移动该棋子。
        /// </summary>
        /// <param name="pIndex">棋子的序号</param>
        /// <param name="x">棋子的棋盘X坐标</param>
        /// <param name="y">棋子的棋盘Y坐标</param>
        /// <returns></returns>
        private bool CanPieceMove(int pIndex, int x, int y) {
            int side = pIndex < 13 ? Constants.SIDE_WHITE : Constants.SIDE_BLACK;

            // 在没有下蜂王之前不能移动。
            if (!IsQueenPlaced(side)) {
                return false;
            }

            // 在移动之后会破坏棋子连续性的情况下不能移动。
            if (IsMoveCanBreak(pIndex)) {
                return false;
            }

            return true;
        }

        /// <summary>
        /// 将该点走指定步数的下一个贴边走法写入走法临时数组。
        /// </summary>
        /// <param name="x">棋子的棋盘X坐标</param>
        /// <param name="y">棋子的棋盘Y坐标</param>
        /// <param name="count">移动的步数，如果不限步数传人-2</param>
        private void GetBorderMoves(int x, int y, int count) {
            List<int> indexes = GetPiecesIndexesAt(x, y);
            int tmpX = pieces[indexes[0], 0];
            int tmpY = pieces[indexes[0], 1];
            pieces[indexes[0], 0] = Constants.PIECES_POS_STACK;
            pieces[indexes[0], 1] = Constants.PIECES_POS_STACK;
            GetNextBorderMove(x, y, -1, count, true);
            tmpHintMoves2.Clear();
            CopyList(tmpHintMoves, tmpHintMoves2);
            tmpHintMoves.Clear();

            // 如果不限制步数，只需要走一次顺时针判断即可，不需要再判断逆时针方向。
            if (count > 0) {
                GetNextBorderMove(x, y, -1, count, false);
            }

            // 合并顺时针和逆时针的走法。
            MergeList(tmpHintMoves2, tmpHintMoves);

            pieces[indexes[0], 0] = tmpX;
            pieces[indexes[0], 1] = tmpY;
        }

        /// <summary>
        /// 将该点按照指定方向、走指定步数的下一个贴边走法写入走法临时数组。
        /// </summary>
        /// <param name="x">该点棋盘的X坐标</param>
        /// <param name="y">该点棋盘的Y坐标</param>
        /// <param name="depth">当前递归的深度</param>
        /// <param name="count">移动的步数，如果不限步数传人-2</param>
        /// <param name="clockwise">是否是顺时针方向</param>
        private void GetNextBorderMove(int x, int y, int depth, int count, bool clockwise) {
            int offsetX;
            int offsetY;
            int nextX;
            int nextY;

            // 当递归深度达到指定移动步数时返回。
            if (depth == count) {
                return;
            }
            if (!IsListContainsPoint(tmpHintMoves, x, y)) {

                // 蜘蛛的中途走法不添加
                if (count == 3) {
                    if (depth == 2) {
                        tmpHintMoves.Add(new Point(x, y));
                        return;
                    }
                } else {
                    if (depth > -1) {
                        tmpHintMoves.Add(new Point(x, y));
                    }
                }

                // 获取当前位置的相邻6个位置。
                // 如果相邻位置有棋子，那么通过当前点和相邻棋子的位置关系，结合走子方向获取下一个移动的位置，
                // 如果下一个位置没有棋子就添加该走法。
                // 如果下一个位置有棋子则递归进行上述的判断。
                List<Point> neighbors = GetNeighborPoints(x, y);
                depth++;
                foreach (Point p in neighbors) {
                    List<int> ps = GetPiecesIndexesAt(p.X, p.Y);
                    if (ps.Count > 0) {
                        offsetX = x - p.X;
                        offsetY = y - p.Y;
                        if (offsetX == 2 && offsetY == 0) {
                            if (clockwise) {
                                nextX = p.X + 1;
                                nextY = p.Y + 1;
                            } else {
                                nextX = p.X + 1;
                                nextY = p.Y - 1;
                            }
                            if (HasPiece(nextX, nextY) == 0) {
                                if (!CanNotMoveIn(x, y, nextX, nextY)) {
                                    GetNextBorderMove(nextX, nextY, depth, count, clockwise);
                                }
                            } else {
                                continue;
                            }
                        }
                        if (offsetX == 1 && offsetY == 1) {
                            if (clockwise) {
                                nextX = p.X - 1;
                                nextY = p.Y + 1;
                            } else {
                                nextX = p.X + 2;
                                nextY = p.Y;
                            }
                            if (HasPiece(nextX, nextY) == 0) {
                                if (!CanNotMoveIn(x, y, nextX, nextY)) {
                                    GetNextBorderMove(nextX, nextY, depth, count, clockwise);
                                }
                            } else {
                                continue;
                            }
                        }
                        if (offsetX == -1 && offsetY == 1) {
                            if (clockwise) {
                                nextX = p.X - 2;
                                nextY = p.Y;
                            } else {
                                nextX = p.X + 1;
                                nextY = p.Y + 1;
                            }
                            if (HasPiece(nextX, nextY) == 0) {
                                if (!CanNotMoveIn(x, y, nextX, nextY)) {
                                    GetNextBorderMove(nextX, nextY, depth, count, clockwise);
                                }
                            } else {
                                continue;
                            }
                        }
                        if (offsetX == -2 && offsetY == 0) {
                            if (clockwise) {
                                nextX = p.X - 1;
                                nextY = p.Y - 1;
                            } else {
                                nextX = p.X - 1;
                                nextY = p.Y + 1;
                            }
                            if (HasPiece(nextX, nextY) == 0) {
                                if (!CanNotMoveIn(x, y, nextX, nextY)) {
                                    GetNextBorderMove(nextX, nextY, depth, count, clockwise);
                                }
                            } else {
                                continue;
                            }
                        }
                        if (offsetX == -1 && offsetY == -1) {
                            if (clockwise) {
                                nextX = p.X + 1;
                                nextY = p.Y - 1;
                            } else {
                                nextX = p.X - 2;
                                nextY = p.Y;
                            }
                            if (HasPiece(nextX, nextY) == 0) {
                                if (!CanNotMoveIn(x, y, nextX, nextY)) {
                                    GetNextBorderMove(nextX, nextY, depth, count, clockwise);
                                }
                            } else {
                                continue;
                            }
                        }
                        if (offsetX == 1 && offsetY == -1) {
                            if (clockwise) {
                                nextX = p.X + 2;
                                nextY = p.Y;
                            } else {
                                nextX = p.X - 1;
                                nextY = p.Y - 1;
                            }
                            if (HasPiece(nextX, nextY) == 0) {
                                if (!CanNotMoveIn(x, y, nextX, nextY)) {
                                    GetNextBorderMove(nextX, nextY, depth, count, clockwise);
                                }
                            } else {
                                continue;
                            }
                        }
                    }
                }
            }
            return;
        }

        private void CopyList(List<Point> fList, List<Point> tList) {
            foreach (Point p in fList) {
                tList.Add(p);
            }
        }

        private void MergeList(List<Point> fList, List<Point> tList) {
            foreach (Point p in fList) {
                if (!IsListContainsPoint(tList, p.X, p.Y)) {
                    tList.Add(p);
                }
            }
        }

        private void CreateQueenHintMoves(int pIndex, int x, int y) {
            tmpHintMoves.Clear();
            if (CanPieceMove(pIndex, x, y)) {
                GetBorderMoves(x, y, 1);
                MergeList(tmpHintMoves, hintMoves);
            }
        }

        private void CreateAntHintMoves(int pIndex, int x, int y) {
            tmpHintMoves.Clear();
            if (CanPieceMove(pIndex, x, y)) {
                GetBorderMoves(x, y, -2);
                foreach (Point p in tmpHintMoves) {
                    if (p.X == x && p.Y == y) {
                        tmpHintMoves.Remove(p);
                        break;
                    }
                }
            }
            MergeList(tmpHintMoves, hintMoves);
        }

        private void CreateSpiderHintMoves(int pIndex, int x, int y) {
            tmpHintMoves.Clear();
            if (CanPieceMove(pIndex, x, y)) {
                GetBorderMoves(x, y, 3);
                foreach (Point p in tmpHintMoves) {
                    if (p.X == x && p.Y == y) {
                        tmpHintMoves.Remove(p);
                        break;
                    }
                }
                MergeList(tmpHintMoves, hintMoves);
            }
        }

        private void CreateGasshopperHintMoves(int pIndex, int x, int y) {
            tmpHintMoves.Clear();
            int nextX;
            int nextY;
            int offsetX;
            int offsetY;
            if (CanPieceMove(pIndex, x, y)) {
                List<Point> neighbors = GetNeighborPoints(x, y);
                foreach (Point p in neighbors) {
                    if (HasPiece(p.X, p.Y) > 0) {
                        offsetX = p.X - x;
                        offsetY = p.Y - y;
                        nextX = p.X;
                        nextY = p.Y;
                        do {
                            nextX += offsetX;
                            nextY += offsetY;
                        } while (HasPiece(nextX, nextY) > 0);
                        tmpHintMoves.Add(new Point(nextX, nextY));
                    }
                }
            }
            MergeList(tmpHintMoves, hintMoves);
        }

        private void CreateLadybugHintMoves(int pIndex, int x, int y) {
            tmpHintMoves.Clear();
            if (CanPieceMove(pIndex, x, y)) {
                tmpHintMoves.Clear();

                List<int> indexes = GetPiecesIndexesAt(x, y);
                int tmpX = pieces[indexes[0], 0];
                int tmpY = pieces[indexes[0], 1];
                pieces[indexes[0], 0] = Constants.PIECES_POS_STACK;
                pieces[indexes[0], 1] = Constants.PIECES_POS_STACK;

                List<Point> neighbors = GetNeighborPoints(x, y);
                foreach (Point p in neighbors) {
                    if (HasPiece(p.X, p.Y) > 0 && !CanNotMoveIn(x, y, p.X, p.Y)) {
                        List<Point> neighbors2 = GetNeighborPoints(p.X, p.Y);
                        foreach (Point p2 in neighbors2) {
                            if (HasPiece(p2.X, p2.Y) > 0 && !CanNotMoveIn(x, y, p.X, p.Y)) {
                                List<Point> neighbors3 = GetNeighborPoints(p2.X, p2.Y);
                                foreach (Point p3 in neighbors3) {
                                    if (HasPiece(p3.X, p3.Y) == 0 && !CanNotMoveIn(x, y, p.X, p.Y)) {
                                        if (!IsListContainsPoint(tmpHintMoves, p3.X, p3.Y)) {
                                            if (p3.X != x || p3.Y != y) {
                                                tmpHintMoves.Add(p3);
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
                MergeList(tmpHintMoves, hintMoves);

                pieces[indexes[0], 0] = tmpX;
                pieces[indexes[0], 1] = tmpY;
            }
        }

        private void CreateBeetleHintMoves(int pIndex, int x, int y) {
            tmpHintMoves.Clear();
            if (CanPieceMove(pIndex, x, y)) {
                GetBorderMoves(x, y, 1);
                MergeList(tmpHintMoves, hintMoves);
                GetUpDownMoves(pIndex, x, y);
                MergeList(tmpHintMoves, hintMoves);
            }
        }

        private void CreateMosquitoHintMoves(int pIndex, int x, int y) {
            tmpHintMoves.Clear();
            if (CanPieceMove(pIndex, x, y)) {
                if (top[pIndex] > 1) {
                    CreateBeetleHintMoves(pIndex, x, y);
                } else {
                    List<Point> neighbors = GetNeighborPoints(x, y);
                    foreach (Point p in neighbors) {
                        int index = GetBoardTopPieceIndex(p.X, p.Y);
                        switch (index) {
                            case 0:
                            case 13: CreateQueenHintMoves(pIndex, x, y); break;
                            case 1:
                            case 2:
                            case 3:
                            case 14:
                            case 15:
                            case 16: CreateAntHintMoves(pIndex, x, y); break;
                            case 4:
                            case 5:
                            case 6:
                            case 17:
                            case 18:
                            case 19: CreateGasshopperHintMoves(pIndex, x, y); break;
                            case 7:
                            case 8:
                            case 20:
                            case 21: CreateBeetleHintMoves(pIndex, x, y); break;
                            case 9:
                            case 10:
                            case 22:
                            case 23: CreateSpiderHintMoves(pIndex, x, y); break;
                            //case 11:
                            //case 24: if(hasMosquito) { CreateMosquitoHintMoves(pIndex, x, y); } break;
                            case 12:
                            case 25: if (hasLadybug) { CreateLadybugHintMoves(pIndex, x, y); } break;
                        }
                    }
                }
            }
        }

        private void GetUpDownMoves(int pIndex, int x, int y) {
            tmpHintMoves.Clear();

            List<Point> neighbors = GetNeighborPoints(x, y);
            foreach (Point p in neighbors) {
                if (top[pIndex] > 1) {
                    if (HasPiece(p.X, p.Y) == 0 && !CanNotMoveIn(x, y, p.X, p.Y)) {
                        tmpHintMoves.Add(p);
                    }
                }
                if (HasPiece(p.X, p.Y) > 0 && !CanNotMoveIn(x, y, p.X, p.Y)) {
                    tmpHintMoves.Add(p);
                }
            }
        }

        private int CheckGameOver() {
            bool whiteWins = true;
            bool blackWins = true;
            List<Point> neighbors;
            if (pieces[0, 0] != Constants.PIECES_POS_STACK) {
                neighbors = GetNeighborPoints(pieces[0, 0], pieces[0, 1]);
                foreach (Point p in neighbors) {
                    if (HasPiece(p.X, p.Y) == 0) {
                        whiteWins = false;
                        break;
                    }
                }
            } else {
                whiteWins = false;
            }
            if (pieces[13, 0] != Constants.PIECES_POS_STACK) {
                neighbors = GetNeighborPoints(pieces[13, 0], pieces[13, 1]);
                foreach (Point p in neighbors) {
                    if (HasPiece(p.X, p.Y) == 0) {
                        blackWins = false;
                        break;
                    }
                }
            } else {
                blackWins = false;
            }
            if (whiteWins) {
                if (blackWins) {
                    return Constants.GAME_DRAWS;
                } else {
                    return Constants.GAME_WHITE_WINS;
                }
            } else {
                if (blackWins) {
                    return Constants.GAME_BLACK_WINS;
                }
            }
            return Constants.GAME_NONE;
        }

        #endregion

        #region 窗体/面板事件

        private void FrmMain_Load(object sender, EventArgs e) {
            ResetGame();
            CenterScreen();
        }

        private void FrmMain_Paint(object sender, PaintEventArgs e) {
            Graphics g = e.Graphics;
            g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
            g.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality;
            g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;

            g.Clear(bgColor);
            DrawGameInfo(g);
            DrawMouseCoord(g);

            DrawBoard(g, false);
            DrawLastMoveHighlight(g, false);
            DrawBoardSelected(g, false);
            DrawGameOver(g, false);
            DrawHintMoves(g, false);

            DrawBoard(g, true);
            DrawLastMoveHighlight(g, true);
            DrawBoardSelected(g, true);
            DrawGameOver(g, true);
            DrawHintMoves(g, true);

            DrawEditMode(g);
        }

        private void FrmMain_Resize(object sender, EventArgs e) {
            RedrawBoard();
        }

        private void FrmMain_Deactivate(object sender, EventArgs e) {
            HoverEnd();
        }

        private void FrmMain_MouseDown(object sender, MouseEventArgs e) {
            if (e.Button == System.Windows.Forms.MouseButtons.Right) {
                if (e.Button == MouseButtons.Right) {
                    HoverBegin();
                }
            }
            if (isGameOver > Constants.GAME_NONE) {
                Point point = GetClickHexCoord(e.X, e.Y);
                if (HasPiece(point.X, point.Y) == 0 && e.Button == System.Windows.Forms.MouseButtons.Left) {
                    canDragMove = !(curMoveIndex == -1 && curEditMoveIndex == -1);
                    tmpStartX = e.X;
                    tmpStartY = e.Y;
                    lastX = centerX;
                    lastY = centerY;
                }
                return;
            }

            if (e.Button == System.Windows.Forms.MouseButtons.Left) {
                Point point = GetClickHexCoord(e.X, e.Y);
                if (hintMoves.Count > 0) {
                    if (IsListContainsPoint(hintMoves, point.X, point.Y)) {
                        InEditMode = true;
                        if (stackSelectedIndex != -1) {
                            AddEditMove(stackPieceSelectedIndex, pieces[stackPieceSelectedIndex, 0], pieces[stackPieceSelectedIndex, 1], point.X, point.Y);
                        } else {
                            AddEditMove(boardPieceSelectedIndex, pieces[boardPieceSelectedIndex, 0], pieces[boardPieceSelectedIndex, 1], point.X, point.Y);
                        }
                        snd.Play();
                        if (frmSearchMoves != null && !frmSearchMoves.IsDisposed) {
                            frmSearchMoves.Close();
                        }
                        return;
                    }
                }
                int count = HasPiece(point.X, point.Y);
                if (count > 0) {
                    int pIndex = GetBoardTopPieceIndex(point.X, point.Y);
                    if (pIndex == boardPieceSelectedIndex) {
                        boardPieceSelectedIndex = -1;
                        boardPieceSelectedX = Constants.PIECES_POS_STACK;
                        boardPieceSelectedY = Constants.PIECES_POS_STACK;
                        hintMoves.Clear();
                        RedrawBoard();
                        return;
                    }
                    if (curSide == Constants.SIDE_WHITE) {
                        if (pIndex < 13) {
                            stackSelectedIndex = -1;
                            stackPieceSelectedIndex = -1;

                            boardPieceSelectedIndex = pIndex;
                            boardPieceSelectedX = point.X;
                            boardPieceSelectedY = point.Y;
                            CreateHintMoves(pIndex, point.X, point.Y);
                        } else {
                            RedrawBoard();
                            return;
                        }
                    } else {
                        if (pIndex >= 13) {
                            stackSelectedIndex = -1;
                            stackPieceSelectedIndex = -1;

                            boardPieceSelectedIndex = pIndex;
                            boardPieceSelectedX = point.X;
                            boardPieceSelectedY = point.Y;
                            CreateHintMoves(pIndex, point.X, point.Y);
                        } else {
                            RedrawBoard();
                            return;
                        }
                    }
                } else {
                    canDragMove = !(curMoveIndex == -1 && curEditMoveIndex == -1);
                    tmpStartX = e.X;
                    tmpStartY = e.Y;
                    lastX = centerX;
                    lastY = centerY;
                }
            }
            RedrawBoard();
        }

        private void FrmMain_MouseUp(object sender, MouseEventArgs e) {
            if (e.Button == System.Windows.Forms.MouseButtons.Left) {
                canDragMove = false;
            } else if (e.Button == System.Windows.Forms.MouseButtons.Right) {
                HoverEnd();
            }
            RedrawBoard();
        }

        private void FrmMain_MouseMove(object sender, MouseEventArgs e) {
            if (canDragMove) {
                centerX = lastX + (e.X - tmpStartX);
                centerY = lastY + (e.Y - tmpStartY);
                RedrawBoard();
                return;
            }
            Point point = GetClickHexCoord(e.X, e.Y);
            mouseCoordX = point.X;
            mouseCoordY = point.Y;
            RedrawBoard();
        }

        private void pnlStack_Paint(object sender, PaintEventArgs e) {
            Graphics g = e.Graphics;
            g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
            g.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality;
            g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;

            g.Clear(bgColor);
            DrawStack(g);
            DrawStackSelected(g);
        }

        private void pnlStack_MouseDown(object sender, MouseEventArgs e) {
            if (isGameOver > Constants.GAME_NONE) {
                return;
            }
            if (e.Button == System.Windows.Forms.MouseButtons.Left) {
                int side = Constants.SIDE_NONE;
                int cx = 0;
                int cy = 0;
                float sCenterX = -1;
                float sCenterY = -1;

                if (e.X >= Constants.STACK_WHITE_LEFT - Constants.PIECE_WIDTH / 2 && e.X <= Constants.STACK_WHITE_LEFT - Constants.PIECE_WIDTH / 2 - 1 + 7 * Constants.PIECE_WIDTH) {
                    side = Constants.SIDE_WHITE;
                    sCenterX = Constants.STACK_WHITE_LEFT;
                    sCenterY = Constants.STACK_PIECE_TOP;
                } else if (e.X >= pnlStack.Width - Constants.STACK_BLACK_RIGHT_OFFSET - Constants.PIECE_WIDTH / 2 && e.X <= pnlStack.Width - Constants.STACK_BLACK_RIGHT_OFFSET - Constants.PIECE_WIDTH / 2 - 1 + 7 * Constants.PIECE_WIDTH) {
                    side = Constants.SIDE_BLACK;
                    sCenterX = pnlStack.Width - Constants.STACK_BLACK_RIGHT_OFFSET;
                    sCenterY = Constants.STACK_PIECE_TOP;
                } else {
                    return;
                }

                cx = (int)Math.Floor((e.X - sCenterX) * 2 / Constants.PIECE_WIDTH);
                cy = (int)Math.Floor((e.Y - sCenterY) / Constants.PIECE_OFFSET_Y);
                int coordX, coordY;

                if ((cy + cx) % 2 == 0) {
                    using (GraphicsPath gp = new GraphicsPath()) {
                        PointF[] points = Get4VertexCoords(cx, cy, sCenterX, sCenterY);
                        gp.AddPolygon(points);
                        if (gp.IsVisible(e.X, e.Y)) {
                            coordX = cx;
                            coordY = cy;
                        } else {
                            coordX = cx + 1;
                            coordY = cy + 1;
                        }
                    }
                } else {
                    using (GraphicsPath gp = new GraphicsPath()) {
                        PointF[] points = Get4VertexCoords2(cx, cy, sCenterX, sCenterY);
                        gp.AddPolygon(points);
                        if (gp.IsVisible(e.X, e.Y)) {
                            coordX = cx + 1;
                            coordY = cy;
                        } else {
                            coordX = cx;
                            coordY = cy + 1;
                        }
                    }
                }

                if (coordY == 0) {
                    int sIndex = coordX / 2 + side * 7;
                    int pIndex = GetStackTopPieceIndex(sIndex);
                    if (pIndex < 0) {
                        return;
                    }
                    if ((curSide == Constants.SIDE_WHITE && pIndex >= 0 && pIndex < 13) ||
                        (curSide == Constants.SIDE_BLACK && pIndex >= 13)) {
                        boardPieceSelectedIndex = -1;
                        boardPieceSelectedX = Constants.PIECES_POS_STACK;
                        boardPieceSelectedY = Constants.PIECES_POS_STACK;
                        stackSelectedIndex = (stackSelectedIndex == sIndex) ? -1 : sIndex;
                        stackPieceSelectedIndex = pIndex;
                        if (stackSelectedIndex != -1) {
                            CreatePlaceHintMoves(pIndex);
                        } else {
                            // 重复选择等于取消选择。
                            hintMoves.Clear();
                        }
                    } else {
                        return;
                    }
                    RedrawBoard();
                }
            }
        }

        private void FrmMain_DragEnter(object sender, DragEventArgs e) {
            if (e.Data.GetDataPresent(DataFormats.FileDrop)) {
                e.Effect = DragDropEffects.Link;
            } else {
                e.Effect = DragDropEffects.None;
            }
        }

        private void FrmMain_DragDrop(object sender, DragEventArgs e) {
            string file = ((System.Array)e.Data.GetData(DataFormats.FileDrop)).GetValue(0).ToString();
            if (file.EndsWith("sgf", true, new CultureInfo("en-US"))) {
                helper.ReadSGFFile(this, file);
            } else if (file.EndsWith("hgn", true, new CultureInfo("en-US"))) {
                helper.ReadHGNFile(this, file);
            }
        }

        protected override bool ProcessDialogKey(Keys keyData) {
            switch (keyData) {
                case Keys.Left:
                    PreviousMove();
                    if (frmSearchMoves != null && !frmSearchMoves.IsDisposed) {
                        frmSearchMoves.Close();
                    }
                    return false;
                case Keys.Right:
                    NextMove();
                    if (frmSearchMoves != null && !frmSearchMoves.IsDisposed) {
                        frmSearchMoves.Close();
                    }
                    return false;
                case Keys.Up:
                    if (InEditMode) {
                        GotoMove(editMoves.Count - 1);
                    } else {
                        GotoMove(totalMovesCount - 1);
                    }
                    if (frmSearchMoves != null && !frmSearchMoves.IsDisposed) {
                        frmSearchMoves.Close();
                    }
                    return false;
                case Keys.Down:
                    GotoMove(-1);
                    if (frmSearchMoves != null && !frmSearchMoves.IsDisposed) {
                        frmSearchMoves.Close();
                    }
                    return false;
                case Keys.Space:
                    CenterScreen();
                    break;
                case Keys.Enter:
                    if (InEditMode) {
                        bgColor = Color.White;
                        GotoMove(-1);
                        editMoves.Clear();
                        InEditMode = false;
                        RedrawBoard();

                        if (frmSearchMoves != null && !frmSearchMoves.IsDisposed) {
                            frmSearchMoves.Close();
                        }
                    }
                    break;
                default:
                    break;
            }
            return base.ProcessDialogKey(keyData);
        }

        #endregion

        #region 菜单事件

        private void exitToolStripMenuItem_Click(object sender, EventArgs e) {
            Application.Exit();
        }

        private void aboutToolStripMenuItem_Click(object sender, EventArgs e) {
            SystemSounds.Asterisk.Play();
            FrmAbout frm = new FrmAbout();
            frm.ShowDialog();
        }

        private void gameInfoToolStripMenuItem_Click(object sender, EventArgs e) {
            needDrawGameInfo = gameInfoToolStripMenuItem.Checked;
            RedrawBoard();
        }

        private void mouseCoordToolStripMenuItem_Click(object sender, EventArgs e) {
            needDrawMouseCoord = mouseCoordToolStripMenuItem.Checked;
            RedrawBoard();
        }

        private void onlineReviewRoomToolStripMenuItem_Click(object sender, EventArgs e) {
            if (frmOnlineReviewRoom == null || frmOnlineReviewRoom.IsDisposed) {
                frmOnlineReviewRoom = new FrmOnlineReviewRoom(this);
            }
            frmOnlineReviewRoom.WindowState = FormWindowState.Normal;
            frmOnlineReviewRoom.Show();
            frmOnlineReviewRoom.Focus();
        }

        private void centerScreenToolStripMenuItem_Click(object sender, EventArgs e) {
            CenterScreen();
            RedrawBoard();
        }

        private void backwardToolStripMenuItem_Click(object sender, EventArgs e) {
            PreviousMove();
            if (frmSearchMoves != null && !frmSearchMoves.IsDisposed) {
                frmSearchMoves.Close();
            }
        }

        private void forwardToolStripMenuItem_Click(object sender, EventArgs e) {
            NextMove();
            if (frmSearchMoves != null && !frmSearchMoves.IsDisposed) {
                frmSearchMoves.Close();
            }
        }

        private void backToStartToolStripMenuItem_Click(object sender, EventArgs e) {
            GotoMove(-1);
            if (frmSearchMoves != null && !frmSearchMoves.IsDisposed) {
                frmSearchMoves.Close();
            }
        }

        private void forwardToEndToolStripMenuItem_Click(object sender, EventArgs e) {
            if (InEditMode) {
                GotoMove(editMoves.Count - 1);
            } else {
                GotoMove(totalMovesCount - 1);
            }
            if (frmSearchMoves != null && !frmSearchMoves.IsDisposed) {
                frmSearchMoves.Close();
            }
        }

        private void localGamesDatabaseToolStripMenuItem_Click(object sender, EventArgs e) {
            if (frmLocalGamesDatabase == null || frmLocalGamesDatabase.IsDisposed) {
                frmLocalGamesDatabase = new FrmLocalGamesDatabase(this);
            }
            frmLocalGamesDatabase.WindowState = FormWindowState.Normal;
            frmLocalGamesDatabase.Show();
            frmLocalGamesDatabase.Focus();
        }

        private void highlightLastMoveToolStripMenuItem_Click(object sender, EventArgs e) {
            needDrawLastMoveHighlight = highlightLastMoveToolStripMenuItem.Checked;
            RedrawBoard();
        }

        private void controlBarToolStripMenuItem_Click(object sender, EventArgs e) {
            flpControlBar.Visible = controlBarToolStripMenuItem.Checked;
        }

        private void tournamentRuleToolStripMenuItem_Click(object sender, EventArgs e) {
            string type = gameType;
            bool ladybug = hasLadybug;
            bool mosquito = hasMosquito;
            isTournamentRule = tournamentRuleToolStripMenuItem.Checked;
            ResetGame();
            gameType = type;
            hasLadybug = ladybug;
            hasMosquito = mosquito;
            UpdateStack();
        }

        private void quiteEditModeToolStripMenuItem_Click(object sender, EventArgs e) {
            bgColor = Color.White;
            GotoMove(-1);
            editMoves.Clear();
            InEditMode = false;
            RedrawBoard();

            if (frmSearchMoves != null && !frmSearchMoves.IsDisposed) {
                frmSearchMoves.Close();
            }
        }

        private void openCacheFolderToolStripMenuItem_Click(object sender, EventArgs e) {
            string DOWNLOAD_PATH = Path.GetTempPath() + "hive.reviewer.cache\\";
            if (!Directory.Exists(DOWNLOAD_PATH)) {
                Directory.CreateDirectory(DOWNLOAD_PATH);
            }
            Process.Start(DOWNLOAD_PATH);
        }

        private void standardHiveToolStripMenuItem_Click(object sender, EventArgs e) {
            ResetGame();
            gameType = "hive";
            hasMosquito = false;
            hasLadybug = false;
            UpdateStack();
            RedrawBoard();
        }

        private void hiveWithMosquitoToolStripMenuItem_Click(object sender, EventArgs e) {
            ResetGame();
            gameType = "hive-m";
            hasMosquito = true;
            hasLadybug = false;
            UpdateStack();
            RedrawBoard();
        }

        private void hiveWithLadybugToolStripMenuItem_Click(object sender, EventArgs e) {
            ResetGame();
            gameType = "hive-l";
            hasMosquito = false;
            hasLadybug = true;
            UpdateStack();
            RedrawBoard();
        }

        private void hiveWithMosquitoAndLadybugToolStripMenuItem_Click(object sender, EventArgs e) {
            ResetGame();
            gameType = "hive-lm";
            hasMosquito = true;
            hasLadybug = true;
            UpdateStack();
            RedrawBoard();
        }

        private void saveGameToolStripMenuItem_Click(object sender, EventArgs e) {
            this.Cursor = Cursors.WaitCursor;
            bool saved = SaveGame();
            this.Cursor = Cursors.Default;
            if (saved) {
                MessageBox.Show("Game's been saved to local database!", "OK", MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
        }

        private void searchMovesToolStripMenuItem_Click(object sender, EventArgs e) {
            if (frmSearchMoves == null || frmSearchMoves.IsDisposed) {
                frmSearchMoves = new FrmSearchMoves(this);
            }
            frmSearchMoves.Show();
            frmSearchMoves.Activate();
            frmSearchMoves.Search(GetPositionID(pieces, top), curSide, gameType);
        }

        private void saveAsHGNFileToolStripMenuItem_Click(object sender, EventArgs e) {
            this.Cursor = Cursors.WaitCursor;
            bool saved = SaveGameAsHGN();
            this.Cursor = Cursors.Default;
            if (saved) {
                MessageBox.Show("Game saved as " + bsid + ".hgn.", "Game Saved", MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
        }

        private void fromHGNFileToolStripMenuItem_Click(object sender, EventArgs e) {
            ofdHGN.Filter = "HGN files(*.HGN)|*.hgn";
            if (ofdHGN.ShowDialog() == DialogResult.OK) {
                helper.ReadHGNFile(this, ofdHGN.FileName);
            }
        }

        private void openApplicationFolderToolStripMenuItem_Click(object sender, EventArgs e) {
            Process.Start(Application.StartupPath);
        }

        private void fromSGFFileToolStripMenuItem_Click(object sender, EventArgs e) {
            ofdSGF.Filter = "SGF files(*.SGF)|*.sgf";
            if (ofdSGF.ShowDialog() == DialogResult.OK) {
                helper.ReadSGFFile(this, ofdSGF.FileName);
            }
        }

        private void btnStart_MouseDown(object sender, MouseEventArgs e) {
            GotoMove(-1);
            if (frmSearchMoves != null && !frmSearchMoves.IsDisposed) {
                frmSearchMoves.Close();
            }
        }

        private void btnBackward_MouseDown(object sender, MouseEventArgs e) {
            PreviousMove();
            if (frmSearchMoves != null && !frmSearchMoves.IsDisposed) {
                frmSearchMoves.Close();
            }
        }

        private void btnForward_MouseDown(object sender, MouseEventArgs e) {
            NextMove();
            if (frmSearchMoves != null && !frmSearchMoves.IsDisposed) {
                frmSearchMoves.Close();
            }
        }

        private void btnEnd_MouseDown(object sender, MouseEventArgs e) {
            if (InEditMode) {
                GotoMove(editMoves.Count - 1);
            } else {
                GotoMove(totalMovesCount - 1);
            }
            if (frmSearchMoves != null && !frmSearchMoves.IsDisposed) {
                frmSearchMoves.Close();
            }
        }

        private void btnQuit_MouseDown(object sender, MouseEventArgs e) {
            bgColor = Color.White;
            GotoMove(-1);
            editMoves.Clear();
            InEditMode = false;
            RedrawBoard();

            if (frmSearchMoves != null && !frmSearchMoves.IsDisposed) {
                frmSearchMoves.Close();
            }
        }

        private void btnCenter_MouseDown(object sender, MouseEventArgs e) {
            CenterScreen();
            RedrawBoard();
        }

        private void allToolStripMenuItem_Click(object sender, EventArgs e) {
            LoadGameList(GetPositionID(pieces, top), gameType, "");
        }

        private void whiteWinsToolStripMenuItem_Click(object sender, EventArgs e) {
            LoadGameList(GetPositionID(pieces, top), gameType, "1-0");
        }

        private void drawToolStripMenuItem_Click(object sender, EventArgs e) {
            LoadGameList(GetPositionID(pieces, top), gameType, "1/2-1/2");
        }

        private void blackWinsToolStripMenuItem_Click(object sender, EventArgs e) {
            LoadGameList(GetPositionID(pieces, top), gameType, "0-1");
        }

        private void LoadGameList(string pid, string gameType, string result) {
            if (frmGameLists == null || frmGameLists.IsDisposed) {
                frmGameLists = new FrmGameLists(this);
            }
            frmGameLists.LoadGameList(pid, gameType, result);
            frmGameLists.Show();
            frmGameLists.Activate();
        }

        private void favouriteGamesDatabaseToolStripMenuItem_Click(object sender, EventArgs e) {
            if (frmFavGamesDatabase == null || frmFavGamesDatabase.IsDisposed) {
                frmFavGamesDatabase = new FrmFavGamesDatabase(this);
            }
            frmFavGamesDatabase.WindowState = FormWindowState.Normal;
            frmFavGamesDatabase.Show();
            frmFavGamesDatabase.Focus();
        }

        #endregion

        #region 通用方法

        private bool IsListContainsPoint(List<Point> list, int x, int y) {
            for (int i = 0; i < list.Count; i++) {
                if (list[i].X == x && list[i].Y == y) {
                    return true;
                }
            }
            return false;
        }

        private bool IsHexAdjacentOpponent(int x, int y, int side) {
            List<Point> adjacents = GetNeighborPoints(x, y);
            for (int i = 0; i < 6; i++) {
                if (HasPiece(adjacents[i].X, adjacents[i].Y) == 0) {
                    continue;
                }
                int pIndex = GetBoardTopPieceIndex(adjacents[i].X, adjacents[i].Y);
                if (side == Constants.SIDE_WHITE) {
                    if (pIndex < 13) {
                        continue;
                    } else {
                        return true;
                    }
                } else {
                    if (pIndex >= 13) {
                        continue;
                    } else {
                        return true;
                    }
                }
            }
            return false;
        }

        private List<Point> GetNeighborPoints(int x, int y) {
            List<Point> points = new List<Point>();
            points.Add(new Point(x + 2, y));
            points.Add(new Point(x + 1, y + 1));
            points.Add(new Point(x - 1, y + 1));
            points.Add(new Point(x - 2, y));
            points.Add(new Point(x - 1, y - 1));
            points.Add(new Point(x + 1, y - 1));
            return points;
        }

        private static PointF[] Get4VertexCoords(int cx, int cy, float CenterX, float CenterY) {
            PointF[] points = new PointF[4];
            points[0] = new PointF(cx * Constants.PIECE_WIDTH / 2 + CenterX, cy * Constants.PIECE_OFFSET_Y + CenterY);
            points[1] = new PointF(cx * Constants.PIECE_WIDTH / 2 + CenterX + Constants.PIECE_WIDTH / 2, cy * Constants.PIECE_OFFSET_Y + CenterY);
            points[2] = new PointF(cx * Constants.PIECE_WIDTH / 2 + CenterX + Constants.PIECE_WIDTH / 2, cy * Constants.PIECE_OFFSET_Y + CenterY + Constants.PIECE_HEIGHT / 4);
            points[3] = new PointF(cx * Constants.PIECE_WIDTH / 2 + CenterX, cy * Constants.PIECE_OFFSET_Y + CenterY + Constants.PIECE_HEIGHT / 2);
            return points;
        }

        private static PointF[] Get4VertexCoords2(int cx, int cy, float CenterX, float CenterY) {
            PointF[] points = new PointF[4];
            points[0] = new PointF(cx * Constants.PIECE_WIDTH / 2 + CenterX, cy * Constants.PIECE_OFFSET_Y + CenterY);
            points[1] = new PointF(cx * Constants.PIECE_WIDTH / 2 + CenterX + Constants.PIECE_WIDTH / 2, cy * Constants.PIECE_OFFSET_Y + CenterY);
            points[2] = new PointF(cx * Constants.PIECE_WIDTH / 2 + CenterX + Constants.PIECE_WIDTH / 2, cy * Constants.PIECE_OFFSET_Y + CenterY + Constants.PIECE_HEIGHT / 2);
            points[3] = new PointF(cx * Constants.PIECE_WIDTH / 2 + CenterX, cy * Constants.PIECE_OFFSET_Y + CenterY + Constants.PIECE_HEIGHT / 4);
            return points;
        }

        private static PointF[] Get6VertexCoords(float coordX, float coordY, float radius) {
            PointF[] ps = new PointF[6];
            ps[0] = new PointF(-radius * (float)Math.Cos(Math.PI * 1 / 6) + coordX, radius / 2 + coordY);
            ps[1] = new PointF(0 + coordX, radius + coordY);
            ps[2] = new PointF(radius * (float)Math.Cos(Math.PI * 1 / 6) + coordX, radius / 2 + coordY);
            ps[3] = new PointF(radius * (float)Math.Cos(Math.PI * 1 / 6) + coordX, -radius / 2 + coordY);
            ps[4] = new PointF(0 + coordX, -radius + coordY);
            ps[5] = new PointF(-radius * (float)Math.Cos(Math.PI * 1 / 6) + coordX, -radius / 2 + coordY);
            return ps;
        }

        private int GetStackTopPieceIndex(int sIndex) {
            switch (sIndex) {
                case 0:
                    return (count[sIndex] == 1) ? 0 : -1;
                case 1:
                    return (count[sIndex] > 0) ? count[sIndex] : -1;
                case 2:
                    return (count[sIndex] > 0) ? 3 + count[sIndex] : -1;
                case 3:
                    return (count[sIndex] > 0) ? 6 + count[sIndex] : -1;
                case 4:
                    return (count[sIndex] > 0) ? 8 + count[sIndex] : -1;
                case 5:
                    if (hasMosquito) {
                        return (count[sIndex] > 0) ? 11 : -1;
                    } else if (hasLadybug) {
                        return (count[6] > 0) ? 12 : -1;
                    } else {
                        return -1;
                    }
                case 6:
                    return (hasLadybug && count[sIndex] > 0) ? 12 : -1;
                case 7:
                    return (count[sIndex] == 1) ? 13 : -1;
                case 8:
                    return (count[sIndex] > 0) ? 13 + count[sIndex] : -1;
                case 9:
                    return (count[sIndex] > 0) ? 16 + count[sIndex] : -1;
                case 10:
                    return (count[sIndex] > 0) ? 19 + count[sIndex] : -1;
                case 11:
                    return (count[sIndex] > 0) ? 21 + count[sIndex] : -1;
                case 12:
                    if (hasMosquito) {
                        return (count[sIndex] > 0) ? 24 : -1;
                    } else if (hasLadybug) {
                        return (count[13] > 0) ? 25 : -1;
                    } else {
                        return -1;
                    }
                case 13:
                    return (hasLadybug && count[sIndex] > 0) ? 25 : -1;

            }
            return -1;
        }

        private void InitPieces() {
            for (int i = 0; i < 26; i++) {
                pieces[i, 0] = Constants.PIECES_POS_STACK;
                pieces[i, 1] = Constants.PIECES_POS_STACK;
                pOffsets[i, 0] = 0;
                pOffsets[i, 1] = 0;
                cOffsets[i, 0] = 0;
                cOffsets[i, 1] = 0;
                top[i] = 0;
            }
        }

        private void RedrawBoard() {
            this.Refresh();
        }

        private int HasPiece(int x, int y) {
            if (x == Constants.PIECES_POS_STACK && y == Constants.PIECES_POS_STACK) {
                return -1;
            }
            int count = 0;
            for (int i = 0; i < 26; i++) {
                if (pieces[i, 0] == x && pieces[i, 1] == y) {
                    count++;
                }
            }
            return count;
        }

        public static int HasPiece(int[,] list, int x, int y) {
            if (x == Constants.PIECES_POS_STACK && y == Constants.PIECES_POS_STACK) {
                return -1;
            }
            int count = 0;
            for (int i = 0; i < 26; i++) {
                if (list[i, 0] == x && list[i, 1] == y) {
                    count++;
                }
            }
            return count;
        }

        private int GetBoardTopPieceIndex(int x, int y) {
            for (int i = 0; i < 26; i++) {
                if (pieces[i, 0] != Constants.PIECES_POS_STACK) {
                    if (pieces[i, 0] == x && pieces[i, 1] == y && top[i] == HasPiece(pieces[i, 0], pieces[i, 1])) {
                        return i;
                    }
                }
            }
            return -1;
        }

        private bool CanMoveAnyPieces(int side) {
            int startIndex = side == Constants.SIDE_WHITE ? 0 : 13;
            int endIndex = side == Constants.SIDE_BLACK ? 26 : 13;

            for (int i = startIndex; i < endIndex; i++) {
                if (pieces[i, 0] == Constants.PIECES_POS_STACK) {
                    CreatePlaceHintMoves(i);
                    if (hintMoves.Count > 0) {
                        return true;
                    }
                } else {
                    CreateHintMoves(i, pieces[i, 0], pieces[i, 1]);
                    if (hintMoves.Count > 0) {
                        return true;
                    }
                }
            }

            hintMoves.Clear();
            return false;
        }

        private void SetNextSide(int pIndex) {
            if (pIndex < 13) {
                if (CanMoveAnyPieces(Constants.SIDE_BLACK)) {
                    curSide = Constants.SIDE_BLACK;
                }
            } else {
                if (CanMoveAnyPieces(Constants.SIDE_WHITE)) {
                    curSide = Constants.SIDE_WHITE;
                }
            }
        }

        private void SetCurrentSide(int pIndex) {
            if (pIndex < 13) {
                curSide = Constants.SIDE_WHITE;
            } else {
                curSide = Constants.SIDE_BLACK;
            }
        }

        private void MakeMoveForward(Move move) {
            int count = HasPiece(move.ToX, move.ToY);
            top[move.PieceIndex] = count + 1;
            pieces[move.PieceIndex, 0] = move.ToX;
            pieces[move.PieceIndex, 1] = move.ToY;
            pOffsets[move.PieceIndex, 0] = count + 1 > 1 ? Constants.HOVER_OFFSET_X * count : 0;
            pOffsets[move.PieceIndex, 1] = count + 1 > 1 ? -Constants.HOVER_OFFSET_Y * count : 0;
        }

        private void MakeMoveBackward(Move move) {
            int count = HasPiece(move.FromX, move.FromY);
            top[move.PieceIndex] = count + 1;
            pieces[move.PieceIndex, 0] = move.FromX;
            pieces[move.PieceIndex, 1] = move.FromY;
            pOffsets[move.PieceIndex, 0] = count + 1 > 1 ? Constants.HOVER_OFFSET_X * count : 0;
            pOffsets[move.PieceIndex, 1] = count + 1 > 1 ? -Constants.HOVER_OFFSET_Y * count : 0;
        }

        private void AddEditMove(int pIndex, int fX, int fY, int tX, int tY) {
            Move move = new Move(pIndex, fX, fY, tX, tY);
            if (curEditMoveIndex < 0) {
                editMoves.Clear();
            } else if (curEditMoveIndex < editMoves.Count - 1) {
                editMoves.RemoveRange(curEditMoveIndex + 1, editMoves.Count - curEditMoveIndex - 1);
            }
            editMoves.Add(move);
            MakeMoveForward(move);
            curEditMoveIndex++;
            lastMoveX = move.ToX;
            lastMoveY = move.ToY;
            SetNextSide(pIndex);

            isGameOver = CheckGameOver();
            stackPieceSelectedIndex = -1;
            stackSelectedIndex = -1;
            boardPieceSelectedIndex = -1;
            boardPieceSelectedX = Constants.PIECES_POS_STACK;
            boardPieceSelectedY = Constants.PIECES_POS_STACK;

            hintMoves.Clear();
            UpdateStack();
            RedrawBoard();
        }

        private void NextMove() {
            if (InEditMode) {
                if (curEditMoveIndex < editMoves.Count - 1) {
                    curEditMoveIndex++;
                    Move move = (editMoves[curEditMoveIndex] as Move);
                    MakeMoveForward(move);
                    lastMoveX = move.ToX;
                    lastMoveY = move.ToY;
                    SetNextSide(move.PieceIndex);
                } else {
                    return;
                }
            } else {
                if (curMoveIndex < totalMovesCount - 1) {
                    curMoveIndex++;
                    Move move = (moves[curMoveIndex] as Move);
                    MakeMoveForward(move);
                    lastMoveX = move.ToX;
                    lastMoveY = move.ToY;
                    if (curMoveIndex < totalMovesCount - 2) {
                        SetCurrentSide((moves[curMoveIndex + 1] as Move).PieceIndex);
                    } else {
                        SetNextSide(move.PieceIndex);
                    }
                } else {
                    return;
                }
            }
            isGameOver = CheckGameOver();
            stackPieceSelectedIndex = -1;
            stackSelectedIndex = -1;
            boardPieceSelectedIndex = -1;
            boardPieceSelectedX = Constants.PIECES_POS_STACK;
            boardPieceSelectedY = Constants.PIECES_POS_STACK;

            hintMoves.Clear();
            UpdateStack();
            RedrawBoard();
            snd.Play();
        }

        private void PreviousMove() {
            if (InEditMode) {
                if (curEditMoveIndex > -1) {
                    Move move = (editMoves[curEditMoveIndex] as Move);
                    MakeMoveBackward(move);
                    curEditMoveIndex--;
                    if (curEditMoveIndex > -1) {
                        lastMoveX = (editMoves[curEditMoveIndex] as Move).ToX;
                        lastMoveY = (editMoves[curEditMoveIndex] as Move).ToY;
                    } else {
                        lastMoveX = Constants.PIECES_POS_STACK;
                        lastMoveY = Constants.PIECES_POS_STACK;
                    }
                    SetCurrentSide(move.PieceIndex);
                } else {
                    return;
                }
            } else {
                if (curMoveIndex > -1) {
                    Move move = (moves[curMoveIndex] as Move);
                    MakeMoveBackward(move);
                    curMoveIndex--;
                    if (curMoveIndex > -1) {
                        lastMoveX = (moves[curMoveIndex] as Move).ToX;
                        lastMoveY = (moves[curMoveIndex] as Move).ToY;
                    } else {
                        lastMoveX = Constants.PIECES_POS_STACK;
                        lastMoveY = Constants.PIECES_POS_STACK;
                    }
                    SetCurrentSide(move.PieceIndex);
                } else {
                    return;
                }
            }
            isGameOver = CheckGameOver();
            stackPieceSelectedIndex = -1;
            stackSelectedIndex = -1;
            boardPieceSelectedIndex = -1;
            boardPieceSelectedX = Constants.PIECES_POS_STACK;
            boardPieceSelectedY = Constants.PIECES_POS_STACK;

            hintMoves.Clear();
            UpdateStack();
            RedrawBoard();
            snd.Play();
        }

        private void GotoMove(int index) {
            if (InEditMode) {
                if (index > curEditMoveIndex) {
                    for (int i = curEditMoveIndex; i < index; i++) {
                        if (curEditMoveIndex < editMoves.Count - 1) {
                            curEditMoveIndex++;
                            Move move = (editMoves[curEditMoveIndex] as Move);
                            MakeMoveForward(move);
                            SetNextSide(move.PieceIndex);
                            lastMoveX = move.ToX;
                            lastMoveY = move.ToY;
                        }
                    }
                } else if (index < curEditMoveIndex) {
                    for (int i = curEditMoveIndex - 1; i >= index; i--) {
                        if (curEditMoveIndex > -1) {
                            Move move = (editMoves[curEditMoveIndex] as Move);
                            MakeMoveBackward(move);
                            curEditMoveIndex--;
                            SetCurrentSide(move.PieceIndex);
                            lastMoveX = Constants.PIECES_POS_STACK;
                            lastMoveY = Constants.PIECES_POS_STACK;
                        }
                    }
                } else {
                    return;
                }
            } else {
                if (index > curMoveIndex) {
                    for (int i = curMoveIndex; i < index; i++) {
                        if (curMoveIndex < totalMovesCount - 1) {
                            curMoveIndex++;
                            Move move = (moves[curMoveIndex] as Move);
                            MakeMoveForward(move);
                            SetNextSide(move.PieceIndex);
                            lastMoveX = move.ToX;
                            lastMoveY = move.ToY;
                        }
                    }
                } else if (index < curMoveIndex) {
                    for (int i = curMoveIndex - 1; i >= index; i--) {
                        if (curMoveIndex > -1) {
                            Move move = (moves[curMoveIndex] as Move);
                            MakeMoveBackward(move);
                            curMoveIndex--;
                            SetCurrentSide(move.PieceIndex);
                            lastMoveX = Constants.PIECES_POS_STACK;
                            lastMoveY = Constants.PIECES_POS_STACK;
                        }
                    }
                } else {
                    return;
                }
            }
            isGameOver = CheckGameOver();
            stackPieceSelectedIndex = -1;
            stackSelectedIndex = -1;
            boardPieceSelectedIndex = -1;
            boardPieceSelectedX = Constants.PIECES_POS_STACK;
            boardPieceSelectedY = Constants.PIECES_POS_STACK;

            hintMoves.Clear();
            UpdateStack();
            RedrawBoard();
        }

        private void ResetGame() {
            InitPieces();

            moves.Clear();
            curMoveIndex = -1;
            curEditMoveIndex = -1;
            totalMovesCount = 0;

            bsid = "";
            whitePlayer = "";
            blackPlayer = "";
            gameType = "hive-lm";
            gameTypeName = "";
            gameResult = "";
            whiteTime = "";
            blackTime = "";
            movesString = "";
            hgnx = "";

            hasLadybug = true;
            hasMosquito = true;

            isGameOver = Constants.GAME_NONE;
            InEditMode = false;
            curSide = Constants.SIDE_WHITE;

            stackSelectedIndex = -1;
            stackPieceSelectedIndex = -1;
            boardPieceSelectedIndex = -1;
            boardPieceSelectedX = Constants.PIECES_POS_STACK;
            boardPieceSelectedY = Constants.PIECES_POS_STACK;
            lastMoveX = Constants.PIECES_POS_STACK;
            lastMoveY = Constants.PIECES_POS_STACK;

            editMoves.Clear();
            hintMoves.Clear();

            saveGameToolStripMenuItem.Enabled = false;
            saveAsHGNFileToolStripMenuItem.Enabled = false;

            UpdateStack();

            if (frmSearchMoves != null && !frmSearchMoves.IsDisposed) {
                frmSearchMoves.Close();
            }
        }

        private string GetGameTypeName(string type) {
            if (type == "hive") {
                hasMosquito = false;
                hasLadybug = false;
                return "Standard Hive";
            } else if (type == "hive-l") {
                hasLadybug = true;
                hasMosquito = false;
                return "Hive with Ladybug";
            } else if (type == "hive-m") {
                hasLadybug = false;
                hasMosquito = true;
                return "Hive with Mosquito";
            } else if (type == "hive-lm") {
                hasLadybug = true;
                hasMosquito = true;
                return "Hive with Mosquito and Ladybug";
            }
            return "";
        }

        private void ShowSpyWindow(int x, int y) {
            for (int i = 0; i < 7; i++) {
                spyPieces[i] = Constants.PIECES_POS_STACK;
            }
            for (int i = 0; i < 26; i++) {
                if (pieces[i, 0] == x && pieces[i, 1] == y) {
                    spyPieces[top[i] - 1] = i;
                }
            }
            needDrawSpyWindow = true;
        }

        private Point GetClickHexCoord(int mouseX, int mouseY) {
            int cx = (int)Math.Floor((mouseX - centerX) * 2 / Constants.PIECE_WIDTH);
            int cy = (int)Math.Floor((mouseY - centerY) / Constants.PIECE_OFFSET_Y);
            int coordX, coordY;
            PointF[] points;

            if ((cy + cx) % 2 == 0) {
                using (GraphicsPath gp = new GraphicsPath()) {
                    points = Get4VertexCoords(cx, cy, centerX, centerY);
                    gp.AddPolygon(points);
                    if (gp.IsVisible(mouseX, mouseY)) {
                        coordX = cx;
                        coordY = cy;
                        return new Point(coordX, coordY);
                    } else {
                        coordX = cx + 1;
                        coordY = cy + 1;
                        return new Point(coordX, coordY);
                    }
                }
            } else {
                using (GraphicsPath gp = new GraphicsPath()) {
                    points = Get4VertexCoords2(cx, cy, centerX, centerY);
                    gp.AddPolygon(points);
                    if (gp.IsVisible(mouseX, mouseY)) {
                        coordX = cx + 1;
                        coordY = cy;
                        return new Point(coordX, coordY);
                    } else {
                        coordX = cx;
                        coordY = cy + 1;
                        return new Point(coordX, coordY);
                    }
                }
            }
        }

        private void UpdateStack() {
            for (int i = 0; i < 14; i++) {
                count[i] = 0;
            }

            for (int i = 0; i < 26; i++) {
                if (pieces[i, 0] == Constants.PIECES_POS_STACK) {
                    switch (i) {
                        case 0: count[0]++; break;
                        case 1:
                        case 2:
                        case 3: count[1]++; break;
                        case 4:
                        case 5:
                        case 6: count[2]++; break;
                        case 7:
                        case 8: count[3]++; break;
                        case 9:
                        case 10: count[4]++; break;
                        case 11: if (hasMosquito) { count[5]++; } break;
                        case 12: if (hasLadybug) { count[6]++; } break;
                        case 13: count[7]++; break;
                        case 14:
                        case 15:
                        case 16: count[8]++; break;
                        case 17:
                        case 18:
                        case 19: count[9]++; break;
                        case 20:
                        case 21: count[10]++; break;
                        case 22:
                        case 23: count[11]++; break;
                        case 24: if (hasMosquito) { count[12]++; } break;
                        case 25: if (hasLadybug) { count[13]++; } break;
                    }
                }
            }
        }

        private void CenterScreen() {
            centerX = (this.ClientSize.Width - Constants.PIECE_WIDTH) / 2;
            centerY = (this.ClientSize.Height - pnlStack.Height - meuMain.Height) / 2;
            RedrawBoard();
        }

        public static string GetPositionID(int[,] piecesList, int[] topList) {
            StringBuilder sb = new StringBuilder();

            for (int i = 0; i < 26; i++) {
                sb.Append(piecesList[i, 0] + "," + piecesList[i, 1] + ",");
            }
            for (int j = 0; j < 26; j++) {
                sb.Append(topList[j] + ",");
            }

            MD5 md5 = MD5.Create();
            byte[] bytes = md5.ComputeHash(Encoding.Default.GetBytes(sb.ToString()));

            return BitConverter.ToString(bytes).Replace("-", "");
        }

        public bool SaveGame() {
            if (dbHelper.HasGame(bsid)) {
                MessageBox.Show("Game already exists in local database!", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                this.Cursor = Cursors.Default;
                return false;
            }
            dbHelper.AddGame(bsid, gameType, DateTime.Parse(bsid.Substring(bsid.Length - 15, 10)), Int32.Parse(bsid.Substring(bsid.Length - 4)), whitePlayer, blackPlayer, gameResult, whiteTime, blackTime, movesString.TrimEnd(), totalMovesCount);
            dbHelper.AddOpenings(moves, bsid, gameType, gameResult);

            return true;
        }

        private bool SaveGameAsHGN() {
            MD5 md5 = MD5.Create();
            string hgn = Convert.ToBase64String(Encoding.Default.GetBytes(hgnx));
            byte[] bs = md5.ComputeHash(Encoding.Default.GetBytes(hgn));
            hgn = BitConverter.ToString(bs).Replace("-", "") + hgn;

            if (bsid != "") {
                string path = Application.StartupPath + "\\" + bsid + ".hgn";
                if (!File.Exists(path)) {
                    FileStream fs = File.Create(path);
                    fs.Close();
                }
                StreamWriter sw = new StreamWriter(path, false, Encoding.Default);
                sw.Write(hgn);
                sw.Close();
                sw.Dispose();
                return true;
            }
            return false;
        }

        public void PlaySearchMove(string move) {
            string[] moveArray = move.Split(',');
            Move mov = new Move(Int32.Parse(moveArray[0]), Int32.Parse(moveArray[1]), Int32.Parse(moveArray[2]), Int32.Parse(moveArray[3]), Int32.Parse(moveArray[4]));

            InEditMode = true;
            AddEditMove(mov.PieceIndex, mov.FromX, mov.FromY, mov.ToX, mov.ToY);
            RedrawBoard();
            snd.Play();

            frmSearchMoves.Search(GetPositionID(pieces, top), curSide, gameType);
        }

        public void PlaySearchMoveBackward() {
            if (curEditMoveIndex == -1) {
                return;
            }
            inEditMode = true;
            if (curEditMoveIndex >= 0) {
                PreviousMove();
                RedrawBoard();
                snd.Play();

                frmSearchMoves.Search(GetPositionID(pieces, top), curSide, gameType);
            }
        }

        #endregion

        /// <summary>
        /// 提起所有第二层以上棋子。
        /// </summary>
        private void HoverBegin() {
            for (int i = 0; i < 26; i++) {
                cOffsets[i, 0] = pOffsets[i, 0];
                cOffsets[i, 1] = pOffsets[i, 1];
            }
            Invalidate();
        }

        /// <summary>
        /// 放下所有棋子到原位。
        /// </summary>
        private void HoverEnd() {
            for (int i = 0; i < 26; i++) {
                cOffsets[i, 0] = 0;
                cOffsets[i, 1] = 0;
            }
            Invalidate();
        }

        public void LoadDecodedHGNX(string hgnx) {
            ResetGame();

            this.hgnx = hgnx;
            string[] hgnArray = hgnx.Split(' ');

            bsid = hgnArray[0];
            gameType = hgnArray[1];
            gameTypeName = GetGameTypeName(gameType);
            whitePlayer = hgnArray[2];
            blackPlayer = hgnArray[3];
            gameResult = hgnArray[4];
            whiteTime = hgnArray[5];
            blackTime = hgnArray[6];
            movesString = "";

            string[] move;
            for (int i = 7; i < hgnArray.Length; i++) {
                move = hgnArray[i].Split(',');
                moves.Add(new Move(Int32.Parse(move[0]), Int32.Parse(move[1]), Int32.Parse(move[2]), Int32.Parse(move[3]), Int32.Parse(move[4])));
                movesString += move[0] + "," + move[1] + "," + move[2] + "," + move[3] + "," + move[4] + " ";
            }

            totalMovesCount = moves.Count;

            saveGameToolStripMenuItem.Enabled = true;
            saveAsHGNFileToolStripMenuItem.Enabled = true;

            UpdateStack();
            RedrawBoard();
            SystemSounds.Beep.Play();
            this.Focus();
        }
    }
}
